home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2000 / MacHack 2000.toast / pc / The Hacks / MacHacksBug / Python 1.5.2c1 / Tools / idle / UndoDelegator.py < prev    next >
Encoding:
Python Source  |  2000-06-23  |  7.7 KB  |  282 lines

  1. import sys
  2. import string
  3. from Tkinter import *
  4. from Delegator import Delegator
  5.  
  6. #$ event <<redo>>
  7. #$ win <Control-y>
  8. #$ unix <Alt-z>
  9.  
  10. #$ event <<undo>>
  11. #$ win <Control-z>
  12. #$ unix <Control-z>
  13.  
  14. #$ event <<dump-undo-state>>
  15. #$ win <Control-backslash>
  16. #$ unix <Control-backslash>
  17.  
  18.  
  19. class UndoDelegator(Delegator):
  20.  
  21.     max_undo = 1000
  22.  
  23.     def __init__(self):
  24.         Delegator.__init__(self)
  25.         self.reset_undo()
  26.  
  27.     def setdelegate(self, delegate):
  28.         if self.delegate is not None:
  29.             self.unbind("<<undo>>")
  30.             self.unbind("<<redo>>")
  31.             self.unbind("<<dump-undo-state>>")
  32.         Delegator.setdelegate(self, delegate)
  33.         if delegate is not None:
  34.             self.bind("<<undo>>", self.undo_event)
  35.             self.bind("<<redo>>", self.redo_event)
  36.             self.bind("<<dump-undo-state>>", self.dump_event)
  37.  
  38.     def dump_event(self, event):
  39.         from pprint import pprint
  40.         pprint(self.undolist[:self.pointer])
  41.         print "pointer:", self.pointer,
  42.         print "saved:", self.saved,
  43.         print "can_merge:", self.can_merge,
  44.         print "get_saved():", self.get_saved()
  45.         pprint(self.undolist[self.pointer:])
  46.         return "break"
  47.  
  48.     def reset_undo(self):
  49.         self.was_saved = -1
  50.         self.pointer = 0
  51.         self.undolist = []
  52.         self.set_saved(1)
  53.  
  54.     def set_saved(self, flag):
  55.         if flag:
  56.             self.saved = self.pointer
  57.         else:
  58.             self.saved = -1
  59.         self.can_merge = 0
  60.         self.check_saved()
  61.  
  62.     def get_saved(self):
  63.         return self.saved == self.pointer
  64.  
  65.     saved_change_hook = None
  66.  
  67.     def set_saved_change_hook(self, hook):
  68.         self.saved_change_hook = hook
  69.  
  70.     was_saved = -1
  71.  
  72.     def check_saved(self):
  73.         is_saved = self.get_saved()
  74.         if is_saved != self.was_saved:
  75.             self.was_saved = is_saved
  76.             if self.saved_change_hook:
  77.                 self.saved_change_hook()
  78.  
  79.     def insert(self, index, chars, tags=None):
  80.         self.addcmd(InsertCommand(index, chars, tags))
  81.  
  82.     def delete(self, index1, index2=None):
  83.         self.addcmd(DeleteCommand(index1, index2))
  84.  
  85.     def addcmd(self, cmd):
  86.         cmd.do(self.delegate)
  87.         if self.can_merge and self.pointer > 0:
  88.             lastcmd = self.undolist[self.pointer-1]
  89.             if lastcmd.merge(cmd):
  90.                 return
  91.         self.undolist[self.pointer:] = [cmd]
  92.         if self.saved > self.pointer:
  93.             self.saved = -1
  94.         self.pointer = self.pointer + 1
  95.         if len(self.undolist) > self.max_undo:
  96.             ##print "truncating undo list"
  97.             del self.undolist[0]
  98.             self.pointer = self.pointer - 1
  99.             if self.saved >= 0:
  100.                 self.saved = self.saved - 1
  101.         self.can_merge = 1
  102.         self.check_saved()
  103.  
  104.     def undo_event(self, event):
  105.         if self.pointer == 0:
  106.             self.bell()
  107.             return "break"
  108.         cmd = self.undolist[self.pointer - 1]
  109.         cmd.undo(self.delegate)
  110.         self.pointer = self.pointer - 1
  111.         self.can_merge = 0
  112.         self.check_saved()
  113.         return "break"
  114.  
  115.     def redo_event(self, event):
  116.         if self.pointer >= len(self.undolist):
  117.             self.bell()
  118.             return "break"
  119.         cmd = self.undolist[self.pointer]
  120.         cmd.redo(self.delegate)
  121.         self.pointer = self.pointer + 1
  122.         self.can_merge = 0
  123.         self.check_saved()
  124.         return "break"
  125.  
  126.  
  127. class Command:
  128.  
  129.     # Base class for Undoable commands
  130.  
  131.     tags = None
  132.  
  133.     def __init__(self, index1, index2, chars, tags=None):
  134.         self.marks_before = {}
  135.         self.marks_after = {}
  136.         self.index1 = index1
  137.         self.index2 = index2
  138.         self.chars = chars
  139.         if tags:
  140.             self.tags = tags
  141.  
  142.     def __repr__(self):
  143.         s = self.__class__.__name__
  144.         t = (self.index1, self.index2, self.chars, self.tags)
  145.         if self.tags is None:
  146.             t = t[:-1]
  147.         return s + `t`
  148.  
  149.     def do(self, text):
  150.         pass
  151.  
  152.     def redo(self, text):
  153.         pass
  154.  
  155.     def undo(self, text):
  156.         pass
  157.  
  158.     def merge(self, cmd):
  159.         return 0
  160.  
  161.     def save_marks(self, text):
  162.         marks = {}
  163.         for name in text.mark_names():
  164.             if name != "insert" and name != "current":
  165.                 marks[name] = text.index(name)
  166.         return marks
  167.  
  168.     def set_marks(self, text, marks):
  169.         for name, index in marks.items():
  170.             text.mark_set(name, index)
  171.  
  172.  
  173. class InsertCommand(Command):
  174.  
  175.     # Undoable insert command
  176.  
  177.     def __init__(self, index1, chars, tags=None):
  178.         Command.__init__(self, index1, None, chars, tags)
  179.  
  180.     def do(self, text):
  181.         self.marks_before = self.save_marks(text)
  182.         self.index1 = text.index(self.index1)
  183.         if text.compare(self.index1, ">", "end-1c"):
  184.             # Insert before the final newline
  185.             self.index1 = text.index("end-1c")
  186.         text.insert(self.index1, self.chars, self.tags)
  187.         self.index2 = text.index("%s+%dc" % (self.index1, len(self.chars)))
  188.         self.marks_after = self.save_marks(text)
  189.         ##sys.__stderr__.write("do: %s\n" % self)
  190.  
  191.     def redo(self, text):
  192.         text.mark_set('insert', self.index1)
  193.         text.insert(self.index1, self.chars, self.tags)
  194.         self.set_marks(text, self.marks_after)
  195.         text.see('insert')
  196.         ##sys.__stderr__.write("redo: %s\n" % self)
  197.  
  198.     def undo(self, text):
  199.         text.mark_set('insert', self.index1)
  200.         text.delete(self.index1, self.index2)
  201.         self.set_marks(text, self.marks_before)
  202.         text.see('insert')
  203.         ##sys.__stderr__.write("undo: %s\n" % self)
  204.  
  205.     def merge(self, cmd):
  206.         if self.__class__ is not cmd.__class__:
  207.             return 0
  208.         if self.index2 != cmd.index1:
  209.             return 0
  210.         if self.tags != cmd.tags:
  211.             return 0
  212.         if len(cmd.chars) != 1:
  213.             return 0
  214.         if self.chars and \
  215.            self.classify(self.chars[-1]) != self.classify(cmd.chars):
  216.             return 0
  217.         self.index2 = cmd.index2
  218.         self.chars = self.chars + cmd.chars
  219.         return 1
  220.  
  221.     alphanumeric = string.letters + string.digits + "_"
  222.  
  223.     def classify(self, c):
  224.         if c in self.alphanumeric:
  225.             return "alphanumeric"
  226.         if c == "\n":
  227.             return "newline"
  228.         return "punctuation"
  229.  
  230.  
  231. class DeleteCommand(Command):
  232.  
  233.     # Undoable delete command
  234.  
  235.     def __init__(self, index1, index2=None):
  236.         Command.__init__(self, index1, index2, None, None)
  237.  
  238.     def do(self, text):
  239.         self.marks_before = self.save_marks(text)
  240.         self.index1 = text.index(self.index1)
  241.         if self.index2:
  242.             self.index2 = text.index(self.index2)
  243.         else:
  244.             self.index2 = text.index(self.index1 + " +1c")
  245.         if text.compare(self.index2, ">", "end-1c"):
  246.             # Don't delete the final newline
  247.             self.index2 = text.index("end-1c")
  248.         self.chars = text.get(self.index1, self.index2)
  249.         text.delete(self.index1, self.index2)
  250.         self.marks_after = self.save_marks(text)
  251.         ##sys.__stderr__.write("do: %s\n" % self)
  252.  
  253.     def redo(self, text):
  254.         text.mark_set('insert', self.index1)
  255.         text.delete(self.index1, self.index2)
  256.         self.set_marks(text, self.marks_after)
  257.         text.see('insert')
  258.         ##sys.__stderr__.write("redo: %s\n" % self)
  259.  
  260.     def undo(self, text):
  261.         text.mark_set('insert', self.index1)
  262.         text.insert(self.index1, self.chars)
  263.         self.set_marks(text, self.marks_before)
  264.         text.see('insert')
  265.         ##sys.__stderr__.write("undo: %s\n" % self)
  266.  
  267.  
  268. def main():
  269.     from Percolator import Percolator
  270.     root = Tk()
  271.     root.wm_protocol("WM_DELETE_WINDOW", root.quit)
  272.     text = Text()
  273.     text.pack()
  274.     text.focus_set()
  275.     p = Percolator(text)
  276.     d = UndoDelegator()
  277.     p.insertfilter(d)
  278.     root.mainloop()
  279.  
  280. if __name__ == "__main__":
  281.     main()
  282.